﻿#<# 
#THIS SCRIPT CONTAINS FUNCTIONS THAT ARE CALLED FROM TROUBLESHOOTER, RESOLVER AND VERIFIER SCRIPTS 
#
#--Utility/Helper functions:	
#	Pop-Msg
#	ExpandEnvVariable
#	ConvertToPS_Style
#	CreateRegKeys
#	CreateDirectory
#	resolveType
#--Functions used in the resolver script:
#	Do_SetRegValue
#	Do_DeleteRegValue
#	Do_RenameRegValue
#	Do_CopyFile
#	Do_DeleteFile
#	Do_RunCommand
#--Functions used in the verifier script: 
#	verify_SetRegValue
#	verify_DeleteRegValue
#	verify_RenameRegValue
#	verify_CopyFile
#	verify_DeleteFile
##>


Import-LocalizedData -BindingVariable Strings_AppSettings -FileName LocalizationData

# Displays text in a pop-up message box. It can be used for debugging purpose
function Pop-Msg {
	 param([string]$msg ="message",
	 [string]$ttl = "Title",
	 [int]$type = 64) 
	 $popwin = new-object -comobject wscript.shell
	 $null = $popwin.popup($msg,0,$ttl,$type)
	 remove-variable popwin
}

# Determine the validity of xml against the given schema.
# Returns error message if not valid, return NULL if valid  
function checkValidityOfXML
{
  param($xmlPath, $xsdPath) 
  $result = $null  
  try         
   {             
     $settings = New-Object System.Xml.XmlReaderSettings
	 
     #Add the xmlSchema to a schema set.	 
     $settings.Schemas.Add($null, $xsdPath) >> $null
	 
	 #Set validation type to Schema
     $settings.ValidationType = [System.Xml.ValidationType]::Schema 
	 
	 #Create xml reader to access the XML data
     $reader = [System.Xml.XmlReader]::Create($xmlPath, $settings)  
    
	 #Parse the file. This will throw XML exception if the XML is not valid
     while ($reader.Read()){}    
   }         
   catch [System.Xml.XmlException]        
   {             
     $result += $_.Exception.Message	 
   }
   catch [System.Xml.Schema.XmlSchemaException]        
   {             
     $result += $_.Exception.Message	 
   }  
   
   return $result 
}


# Replace the name of environment variable in the given path with the actual value
function ExpandEnvVariable 
{
  param($Path)	
  
  if($Path)
  { 
 	 return [System.Environment]::ExpandEnvironmentVariables($Path)
  }
}


# If registry path is not powershell style, convert it to powershell style
function ConvertToPS_Style
{   param([string]$path)
	# determine path syntax
	If (-not($path.StartsWith(“Registry::”,"CurrentCultureIgnoreCase") -or `
	   		 $path.StartsWith(“HKLM:”,"CurrentCultureIgnoreCase") -or `
	         $path.StartsWith(“HKCU:”,"CurrentCultureIgnoreCase")
			 )
	   )
	{
     #Transform into PowerShell style before passing on 
	 $path = “Registry::” + $path
	}
	return $path
}

# Create a chain of registry keys from each of the items in the given path
function CreateRegKeys 
{     
	param([string]$path)	
	if($path)
	{		
	    $Keys = $path.split("\")			
		$nextLevel = $Keys[0]
		$ValidHives = @("Registry::HKEY_CURRENT_USER","Registry::HKEY_LOCAL_MACHINE",
		  				"Registry::HKEY_CLASSES_ROOT", "Registry::HKEY_USERS", "Registry::HKEY_CURRENT_CONFIG", "HKLM:","HKCU:"
						)		

		IF($ValidHives -notcontains($nextLevel))
		{
		 #Invalid hive name
		 RETURN
		}
		#Create keys recursively
		for( $i=1; ($i -lt $Keys.Length); $i++ )
        {  
			$nextLevel = Join-Path $nextLevel $Keys[$i]            
			if (!(test-path $nextLevel)) 
			{	          
			  New-Item -Path $nextLevel -Force
			}                                               
        }              
    }
}

# Given a directory path, Create the Directory
function CreateDirectory
{
  param($path)
  trap { continue}
  #Determine whether the directory exists 
  if([System.IO.Directory]::Exists($Path) -eq $false)
   {
	#Create the directory
	[System.IO.Directory]::CreateDirectory($Path)
   }
  
}

# Get registry value type and return its powershell equivalent
Function resolveType
{
  param($Type)
  if($Type)
  {
	  Switch($Type)
	  {
	    "REG_SZ"
			{$Type = "String" }		
		"REG_DWORD"
			{$Type = "DWORD"}
		"REG_QWORD"
			{$Type = "QWORD"}
		"REG_MULTI_SZ"
			{$Type = "MultiString"}
		"REG_EXPAND_SZ"
			{$Type = "ExpandString"}		
	  }
  }
 return $Type  
 
}

#-- RESOLVER FUNCTIONS:
#Responsible for <setregvalue> during the resolution phase
 function Do_SetRegValue 
 { 	PARAM($Setting)	
	$Key = ConvertToPS_Style $Setting.Attributes.GetNamedItem("key").value 
    $Value = $Setting.Attributes.GetNamedItem("value").value	
	$Type = $Setting.Attributes.GetNamedItem("type").value	
	$Type = resolveType $Type
	$Data = $Setting.Attributes.GetNamedItem("data").value
	
	# Create registry key if it doesn't exist
	if ( ($Key -ne $null) -and (-not( Test-Path $key)))
	{
	  CreateRegKeys $key
	}
	
	# Determine whether the registry key exists
	if (($Key -ne $null) -and (Test-Path $Key))
	{  
	    $itemProperty = Get-ItemProperty -Path $Key -Name $Value -ErrorAction SilentlyContinue	
		
		# Determine whether the property exists
		if ($itemProperty)
		{ 
		  if(($Type -ieq "ExpandString") -and ($Data -match "%*%")){ $Data = ExpandEnvVariable $Data }
		  
		  # Determine whether the property is not in the default state
		  if(($itemProperty.$Value) -ne $Data)
			{   
			    # Set the property to the default state
				 New-ItemProperty -Path $Key -Name $Value -PropertyType $Type -Value $Data -Force -ErrorAction SilentlyContinue	
			}			
		}
		else
		{ 
		  # Create the property
		  New-ItemProperty -Path $Key -Name $Value -PropertyType $Type -Value $Data -Force -ErrorAction SilentlyContinue
		}
	}	
}
 
#Responsible for <deleteRegValue> during the resolution phase
function Do_DeleteRegValue
{   PARAM($Setting)
	$Key = ConvertToPS_Style $Setting.Attributes.GetNamedItem("key").value
    $Value = $Setting.Attributes.GetNamedItem("value").value
	
	# Determine whether the registry key exists
	if (($Key -ne $null) -and (Test-Path $Key))
	{    
	    $itemProperty = Get-ItemProperty -Path $Key -Name $Value -ErrorAction SilentlyContinue		
		
		# Determine whether the property exists
		if ($itemProperty)
		{ 
		    # Delete the property
			Remove-ItemProperty -Path $Key -Name $Value -Force 	 	
		}	
		
	}
}

#Responsible for <renameRegValue> during the resolution phase
function Do_RenameRegValue
{ 	PARAM($Setting)    
	$Key = ConvertToPS_Style $Setting.Attributes.GetNamedItem("key").value
    $Value = $Setting.Attributes.GetNamedItem("value").value
	$NewValue = $Setting.Attributes.GetNamedItem("newValue").value	
	
	# Determine whether the registry key exists
	if (($Key -ne $null) -and (Test-Path $Key))
	{
		$itemProperty = Get-ItemProperty -Path $Key -Name $Value -ErrorAction SilentlyContinue
		
		# Determine whether the property exists
		if ($itemProperty)
		{  
		   # Rename the property to the new name
			Rename-ItemProperty -Path $Key -Name $Value -NewName $NewValue -Force 
		} 		
   }
}

#Responsible for <CopyFile> during the resolution phase
function Do_CopyFile
{	PARAM($Setting)
	$source = $Setting.Attributes.GetNamedItem("source").value
    $destination = $Setting.Attributes.GetNamedItem("destination").value
	$overwrite = $Setting.Attributes.GetNamedItem("overwrite").value	
	
	# Replace environment variables with the actual values 
	$srcPath = ExpandEnvVariable $source
	$destPath = ExpandEnvVariable $destination
	
	# Create the destination directory if it doesn't exist 
	if (-not(Test-Path $destPath))
	{  
		CreateDirectory($destPath) 
	}	
	
	#Determine whether the source and destination exist
 	if((Test-Path $srcPath)  -and (test-Path $destPath))
	{      
		 if ($overwrite -eq $True)
		 {	# Do copy whether or not the file exists in the destination			
			Copy -Path  $srcPath  -Destination  $destPath -Force
		 }
		 else
		 { # Do copy only if the file doesn't exist in the destination		
			
			# get the file name from the source path
			$file = Split-Path $srcPath -Leaf -Resolve
			$container = Split-Path $srcPath -Parent
			
			foreach ($f in $file)
			{
			  #Check if the file does not exist in the destination directory
			  if(-not(Test-Path (Join-Path $destPath $f)))
				{
					Copy -Path  (join-path $container $f)  -Destination  $destPath -Force
				}
			}			
			
		}		 
	} 
	
}

#Responsible for <DeleteFile> during the resolution phase
function Do_DeleteFile
{  PARAM($Setting)
   $toDelete = $Setting.Attributes.GetNamedItem("path").value 
   $toDeletePath = ExpandEnvVariable $toDelete
    
	# delete file if it exists
	if (Test-Path $toDeletePath)
	{	
		del $toDeletePath -Force 
	} 
	
}

#Responsible for <RunCommand> during the resolution phase
function Do_RunCommand($Setting)
{   
   $commandline = $Setting.Attributes.GetNamedItem("commandline").value  
   
   $commandline = $commandline.Replace("&quot;", """")   
   $ProcessStartInfo = new-object "Diagnostics.ProcessStartInfo"
   $ProcessStartInfo.FileName = "cmd.exe"
   $ProcessStartInfo.Arguments = "/s /c ""$commandline"""
   $ProcessStartInfo.Verb = "runas"
   $ProcessStartInfo.UseShellExecute = $false
   $process = [Diagnostics.Process]::Start($ProcessStartInfo)
   $process.WaitforExit();
   
}


#-- VERIFIER FUNCTIONS:

function verify_SetRegValue
{ PARAM($Setting)
    $Rawkey = $Setting.Attributes.GetNamedItem("key").value
	$Key = ConvertToPS_Style $Rawkey
    $Value = $Setting.Attributes.GetNamedItem("value").value	 
	$Data = $Setting.Attributes.GetNamedItem("data").value
	$Type = $Setting.Attributes.GetNamedItem("type").value	
	$Type = resolveType $Type
	$result = "fixed"
	
	# Determine whether the registry key exists
	if (($Key -ne $null) -and (Test-Path $Key))
	{  
	    $itemProperty = Get-ItemProperty -Path $Key -Name $Value -ErrorAction SilentlyContinue	
		
		# Determine whether the property exists
		if ($itemProperty)
		{
		 #Determine the if the type is "ExpandString", Expand the env variable before passing the data for comparison 
		 if(($Type -ieq "ExpandString") -and ($Data -match "%*%")){ $Data = ExpandEnvVariable $Data }		 
		 # Determine whether the property is not in the default state
		 if(($itemProperty.$Value) -ne $Data) 
		 	{   
			 #error msg:"%value%" could not be set			
			 $result = ($Strings_AppSettings.ID_NAME_VALUE_NOTSET) -Replace "%Value%", $Value
			}
		
		} 
		else
		{ 
		  #error msg:"%value%" could not be set	 
		  $result = ($Strings_AppSettings.ID_NAME_VALUE_NOTSET) -Replace "%Value%", $Value
		}
	}
	else 
	{ 
	  #error msg:"path" could not be found	 
	  $result = ($Strings_AppSettings.ID_NAME_PATH_NOT_FOUND) -Replace"%Path%", $Rawkey	
	}
	 
  return $result
}

function verify_DeleteRegValue
{	PARAM($Setting)
	$Rawkey = $Setting.Attributes.GetNamedItem("key").value
	$Key = ConvertToPS_Style $Rawkey
	$Value = $Setting.Attributes.GetNamedItem("value").value
	$result = "fixed"
	
	# Determine whether the registry key exists
	if (($Key -ne $null) -and (Test-Path $Key)) 
	{ 	
		$itemProperty = Get-ItemProperty -Path $Key -Name $Value -ErrorAction SilentlyContinue
		
		# Determine whether the property exists
		if ($itemProperty -ne $null)
		{	
		   # error msg:"%value%" could not be deleted 		
			$result =($Strings_AppSettings.ID_NAME_DELETE_FAILED) -Replace "%value%", $Value
		}	
	}
	else
	{ 
	  #error msg:"path" could not be found	  
	  $result = ($Strings_AppSettings.ID_NAME_PATH_NOT_FOUND) -Replace "%Path%", $Rawkey
	}
return $result
}

function verify_RenameRegValue
{	PARAM($Setting)
	$Rawkey = $Setting.Attributes.GetNamedItem("key").value
	$Key = ConvertToPS_Style $Rawkey 
	$Value = $Setting.Attributes.GetNamedItem("value").value
	$NewValue = $Setting.Attributes.GetNamedItem("newValue").value		
	$result = "fixed"
	
	# Determine whether the registry key exists
	if (($Key -ne $null) -and (Test-Path $Key))
	{	
		$itemProperty = Get-ItemProperty -Path $Key -Name $NewValue -ErrorAction SilentlyContinue
		
		# Determine whether the property exists
		if ($itemProperty -eq $null)
		{	
		    #error msg:%value%" could not be renamed
			$result = ($Strings_AppSettings.ID_NAME_RENAME_FAILED) -Replace "%Value%", $Value			
		} 	
   }
   else
	{
	 #error msg:%path% could not be found
	 $result = ($Strings_AppSettings.ID_NAME_PATH_NOT_FOUND) -Replace "%Path%", $Rawkey 
	}
  return $result
}


function verify_CopyFile {	
	PARAM($Setting)   
	$source = $Setting.Attributes.GetNamedItem("source").value
 	$destination = $Setting.Attributes.GetNamedItem("destination").value
	
	# Replace environment variables with the actual values
	$srcPath = ExpandEnvVariable $source
	$destPath = ExpandEnvVariable $destination	
	$result = "fixed"	
	
	#Determine whether the source and destination exist
	if((Test-Path $srcPath) -and  (test-Path $destPath)){ 
		# Get the file name from the source path
		$file =  Split-Path $srcPath -Leaf -Resolve
		$container = Split-Path $srcPath -Parent
		$NotCopied = @()
		foreach ($f in $file)
		{
		  #Check if the file does not exist in the destination directory
		  if(-not(Test-Path (Join-Path $destPath $f)))
			{
				$NotCopied += "$f  "
			}
		}
		
		if ($NotCopied)
		{
			# error msg:%file%" could not be copied
			$result = ($Strings_AppSettings.ID_NAME_CPY_FAILED) -Replace "%file%", $NotCopied
		}
		
	}
	else{	
	  if(-not(Test-Path $srcPath) -and (-not(test-Path $destPath))){
	    # error msg: %srcPath% and %destPath% could not be found
	    $srcDest = $srcPath + "," + $destPath 
	  	$result = ($Strings_AppSettings.ID_NAME_PATH_NOT_FOUND) -Replace "%Path%", $srcDest	 	 
	   }
	  elseif(-not(Test-Path $srcPath)){
	     # error msg:%srcPath% could not be found
		 $result = ($Strings_AppSettings.ID_NAME_PATH_NOT_FOUND) -Replace "%Path%", $srcPath
	   }
	  else {
	    # error msg:%destPath% could not be found
	  	$result = ($Strings_AppSettings.ID_NAME_PATH_NOT_FOUND) -Replace "%Path%", $destPath	 
	  }
   }
 return $result
}

function verify_DeleteFile
{	PARAM($Setting)
    $toDelete = $Setting.Attributes.GetNamedItem("path").value
	
	# Replace environment variables with the actual values 
	$deletePath = ExpandEnvVariable $toDelete
    $result = "fixed"  
	
	if(test-Path $deletePath)
	{ 
	  # error msg:%value%" could not be deleted
	  $result = ($Strings_AppSettings.ID_NAME_DELETE_FAILED) -Replace "%value%", $deletePath
	}
  return $result 
}

